% SUBJECT: Professional GEM, Appendix A

% 13.11.1990
%

\subsection{Folder {\tt $\backslash$}}
\nopagebreak

\subsubsection{\tt gemc10.c}
\begin{verbatim} 
#include "portab.h"     /* portable coding conv */
#include "machine.h"    /* machine depndnt conv */
#include "obdefs.h"     /* object definitions   */
#include "gembind.h"    /* gem binding structs  */
#include "taddr.h"

#define M1_ENTER        0x0000
#define M1_EXIT         0x0001

#define BS      0x0008
#define TAB     0x0009
#define CR      0x000D
#define ESC     0x001B
#define BTAB    0x0f00
#define UP      0x4800
#define DOWN    0x5000
#define DEL     0x5300
                          /* Global variables used by */
                          /* 'mapped' functions       */
MLOCAL  GRECT   br_rect;  /* Current break rectangle  */
MLOCAL  WORD    br_mx,
                br_my,
                br_togl;  /* Break mouse posn & flag  */ 
MLOCAL  WORD    fn_obj;   /* Found tabable object     */
MLOCAL  WORD    fn_last;  /* Object tabbing from      */
MLOCAL  WORD    fn_prev;  /* Last EDITABLE obj seen   */
MLOCAL  WORD    fn_dir;   /* 1 = TAB, 0 = BACKTAB     */

/*** Utility routines for new forms manager ***********/

        VOID
objc_toggle(tree, obj)   /* Reverse the SELECT state */
        LONG    tree;    /* of an object, and redraw */
        WORD    obj;     /* it immediately.          */
        {
        WORD    state, newstate;
        GRECT   root, ob_rect;

        objc_xywh(tree, ROOT, &root);
        state = LWGET(OB_STATE(obj));
        newstate = state ^ SELECTED;
        objc_change(tree, obj, 0, root.g_x, root.g_y, 
                root.g_w, root.g_h, newstate, 1);
        }

        VOID             /* If the object is not already */
objc_sel(tree, obj)      /* SELECTED, make it so.        */
        LONG    tree;
        WORD    obj;
        {
        if ( !(LWGET(OB_STATE(obj)) & SELECTED) )
                objc_toggle(tree, obj);
        }

        VOID             /* If the object is SELECTED,   */
objc_dsel(tree, obj)     /* deselect it.                 */
        LONG    tree;
        WORD    obj;
        {
        if (LWGET(OB_STATE(obj)) & SELECTED)
                objc_toggle(tree, obj);
        }

        VOID             /* Return the object's GRECT    */
objc_xywh(tree, obj, p)  /* through 'p'                  */
        LONG    tree;
        WORD    obj;
        GRECT   *p;
        {
        objc_offset(tree, obj, &p->g_x, &p->g_y);
        p->g_w = LWGET(OB_WIDTH(obj));
        p->g_h = LWGET(OB_HEIGHT(obj));
        }


/* Non-cursive traverse of an   */
/* object tree.  This routine   */
/* is described in PRO GEM #5.  */
        VOID
map_tree(tree, this, last, routine)
        LONG            tree;
        WORD            this, last;
        WORD            (*routine)();
        {
        WORD            tmp1;

        tmp1 = this; /* Initialize to impossible value: */
                     /* TAIL won't point to self!       */
                     /* Look until final node, or off   */
                     /* the end of tree                 */ 
        while (this != last && this != NIL)
                     /* Did we 'pop' into this node     */
                     /* for the second time?            */
                if (LWGET(OB_TAIL(this)) != tmp1)
                        {
                        tmp1 = this;
                        /* This is a new node       */
                        this = NIL;
                        /* Apply operation, testing  */
                        /* for rejection of sub-tree */
                        if ((*routine)(tree, tmp1))
                                this = LWGET(OB_HEAD(tmp1));
                                /* Subtree path not taken,   */
                                /* so traverse right         */ 
                        if (this == NIL)
                                this = LWGET(OB_NEXT(tmp1));
                        }
                else            /* Revisiting parent:        */
                                /* No operation, move right  */
                        {
                        tmp1 = this;
                        this = LWGET(OB_NEXT(tmp1));
                        }
        }

        WORD                  /* Find the parent object of    */
get_parent(tree, obj)         /* by traversing right until    */
        LONG            tree; /* we find nodes whose NEXT     */
        WORD            obj;  /* and TAIL links point to      */
        {                     /* each other.                  */
        WORD            pobj;

        if (obj == NIL)
                return (NIL);
        pobj = LWGET(OB_NEXT(obj));
        if (pobj != NIL)
        {
          while( LWGET(OB_TAIL(pobj)) != obj ) 
          {
            obj = pobj;
            pobj = LWGET(OB_NEXT(obj));
          }
        }
        return(pobj);
        } 

        WORD
inside(x, y, pt)  /* determine if x,y is in rectangle */
        WORD            x, y;
        GRECT           *pt;
        {
        if ( (x >= pt->g_x) &&
             (y >= pt->g_y) &&
             (x < pt->g_x + pt->g_w) &&
             (y < pt->g_y + pt->g_h)    )
                return(TRUE);
        else
                return(FALSE);
        } 

 /* compute intersection of two GRECTs */
        WORD
rc_intersect(p1, p2)
        GRECT           *p1, *p2;
        {
        WORD            tx, ty, tw, th;

        tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w);
        th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h);
        tx = max(p2->g_x, p1->g_x);
        ty = max(p2->g_y, p1->g_y);
        p2->g_x = tx;
        p2->g_y = ty;
        p2->g_w = tw - tx;
        p2->g_h = th - ty;
        return( (tw > tx) && (th > ty) );
        }

/* copy source to destination rectangle */
        VOID
rc_copy(psbox, pdbox)
        GRECT   *psbox;
        GRECT   *pdbox;
        {
        pdbox->g_x = psbox->g_x;
        pdbox->g_y = psbox->g_y;
        pdbox->g_w = psbox->g_w;
        pdbox->g_h = psbox->g_h;
        }

/*** "Hot-spot" manager and subroutines  ***/

        WORD
break_x(pxy)
        WORD    *pxy;
        {                    /* Breaking object is right of  */
        if (br_mx < pxy[0])  /* mouse.  Reduce width of      */
                {            /* bounding rectangle.          */
                br_rect.g_w = pxy[0] - br_rect.g_x;
                return (TRUE);
                }
        if (br_mx > pxy[2])  /* Object to left.  Reduce width*/
                {            /* and move rect. to right      */
                br_rect.g_w += br_rect.g_x - pxy[2] - 1;
                br_rect.g_x = pxy[2] + 1;
                return (TRUE);
                }
        return (FALSE);      /* Mouse within object segment. */
        }                    /* Break attempt fails.         */

        WORD
break_y(pxy)
        WORD    *pxy;
        {
        if (br_my < pxy[1])  /* Object below mouse.  Reduce  */
                {            /* height of bounding rect.     */
                br_rect.g_h = pxy[1] - br_rect.g_y;
                return (TRUE);
                }
        if (br_my > pxy[3])  /* Object above mouse.  Reduce  */
                {            /* height and shift downward.   */
                br_rect.g_h += br_rect.g_y - pxy[3] - 1;
                br_rect.g_y = pxy[3] + 1;
                return (TRUE); 
                }
        /* Emergency escape test! Protection vs. turkeys */
        /* who nestnon-selectable objects inside of      */
        /* selectables.                                  */
        if (br_mx >= pxy[0] && br_mx <= pxy[1])
                {                     /* Will X break fail? */
                br_rect.g_x = br_mx;  /* If so, punt!       */
                br_rect.g_y = br_my;
                br_rect.g_w = br_rect.g_h = 1;
                return (TRUE);
                }
        return (FALSE);
        }

        WORD
break_obj(tree, obj)   /* Called once per object to    */
        LONG    tree;  /* check if the bounding rect.  */
        WORD    obj;   /* needs to be modified.        */
        {
        GRECT   s;
        WORD    flags, broken, pxy[4];

        objc_xywh(tree, obj, &s);
        grect_to_array(&s, pxy);
        if (!rc_intersect(&br_rect, &s))
                return (FALSE);
         /* Trivial rejection case       */

        flags = LWGET(OB_FLAGS(obj));
        /* Is this object a potential   */
        /* hot-spot?                    */
        if (flags & HIDETREE)
                return (FALSE);
        if ( !(flags & SELECTABLE) )
                return (TRUE);
        if (LWGET(OB_STATE(obj)) & DISABLED)
                return (TRUE);
        /* This could take two passes   */
        /* if the first break fails.    */
        for (broken = FALSE; !broken; )
                {
                if (br_togl)
                        broken = break_x(pxy);
                else
                        broken = break_y(pxy);
                br_togl = !br_togl;
                }
        return (TRUE);
        }

        WORD  /* Manages mouse rectangle events */
form_hot(tree, hot_obj, mx, my, rect, mode)
        LONG    tree;
        WORD    hot_obj, mx, my, *mode;
        GRECT   *rect;
        {
        GRECT   root;
        WORD    state;

        objc_xywh(tree, ROOT, &root);
        /* If there is already a hot-spot */
        /* turn it off.                   */
        if (hot_obj != NIL)
                objc_toggle(tree, hot_obj);

        /* Mouse has moved outside of     */
        /* the dialog.  Wait for return.  */
        if (!(inside(mx, my, &root)) )
                {
                *mode = M1_ENTER;
                rc_copy(&root, rect);
                return (NIL);
                }
        /* What object is mouse over?     */
        /* (Hit is guaranteed.)           */
        hot_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my);

        /* Is this object a hot-spot?     */
        state = LWGET(OB_STATE(hot_obj));
        if (LWGET(OB_FLAGS(hot_obj)) & SELECTABLE)
        if ( !(state & DISABLED) )
                { /* Yes!  Set up wait state.     */
                *mode = M1_EXIT;
                objc_xywh(tree, hot_obj, rect);
                /* But only toggle if it's not    */
                /* already SELECTED!              */
                if (state & SELECTED)
                        return (NIL);
                else
                        {
                        objc_toggle(tree, hot_obj);
                        return (hot_obj);
                        }
                }

       /* No hot object, so compute    */
       /* mouse bounding rectangle.    */
        rc_copy(&root, &br_rect);
        br_mx = mx;
        br_my = my;
        br_togl = 0;
        map_tree(tree, ROOT, NIL, break_obj);
        /* Then return to wait state.   */
        rc_copy(&br_rect, rect);
        *mode = M1_EXIT;
        return (NIL);
        }

/*** Keyboard manager and subroutines ***************/

        WORD
find_def(tree, obj) /* Check if the object is DEFAULT */
        LONG    tree;
        WORD    obj;
        { /* Is sub-tree hidden?   */
        if (HIDETREE & LWGET(OB_FLAGS(obj)))
                return (FALSE);
        /* Must be DEFAULT and not DISABLED     */
        if (DEFAULT & LWGET(OB_FLAGS(obj)))
        if ( !(DISABLED & LWGET(OB_STATE(obj))) )
                fn_obj = obj;   /* Record object number */
        return (TRUE);
        }

        WORD
find_tab(tree, obj) /* Look for target of TAB operation. */
        LONG    tree;
        WORD    obj;
        { /* Check for hiddens subtree. */
        if (HIDETREE & LWGET(OB_FLAGS(obj)))
                return (FALSE);
        /* If not EDITABLE, who cares?          */
        if ( !(EDITABLE & LWGET(OB_FLAGS(obj))) )
                return (TRUE);
        /* Check for forward tab match          */
        if (fn_dir && fn_prev == fn_last)
                fn_obj = obj;
        /* Check for backward tab match         */
        if (!fn_dir && obj == fn_last)
                fn_obj = fn_prev;
        fn_prev = obj; /* Record object for next call. */
        return (TRUE);
        }       

        WORD
form_keybd(tree, edit_obj, next_obj, kr, out_obj, okr)
        LONG    tree;
        WORD    edit_obj, next_obj, kr, *out_obj, *okr;
        {
        if (LLOBT(kr))     /* If lower byte valid, mask out     */
                kr &= 0xff;/* extended code byte.               */
        fn_dir = 0;        /* Default tab direction if backward.*/
        switch (kr) {
                case CR: /* Zap character. */
                        *okr = 0;
                         /* Look for a DEFAULT object. */
                        fn_obj = NIL;
                        map_tree(tree, ROOT, NIL, find_def);
                        /* If found, SELECT and force exit. */
                        if (fn_obj != NIL)
                                {
                                objc_sel(tree, fn_obj);
                                *out_obj = fn_obj;
                                return (FALSE);
                                }   /* Falls through to     */ 
                case TAB:           /* tab if no default    */
                case DOWN:      
                        fn_dir = 1; /* Set fwd direction    */
                case BTAB:
                case UP:
                        *okr = 0;   /* Zap character        */
                        fn_last = edit_obj;
                        fn_prev = fn_obj = NIL;
                        /* Look for TAB object  */
                        map_tree(tree, ROOT, NIL, find_tab);
                        /* try to wrap around */
                        if (fn_obj == NIL)
                                map_tree(tree, ROOT,
                                         NIL, find_tab);
                        if (fn_obj != NIL)
                                *out_obj = fn_obj;
                        break;
                default: /* Pass other chars     */
                        return (TRUE);
                }
        return (TRUE);
        }

/*** Mouse button manager and subroutines ***************/

        WORD
do_radio(tree, obj)
        LONG    tree;
        WORD    obj;
        {
        GRECT   root;
        WORD    pobj, sobj, state;

        objc_xywh(tree, ROOT, &root);
        pobj = get_parent(tree, obj);
        /* Get the object's parent */

        for (sobj = LWGET(OB_HEAD(pobj)); sobj != pobj;
                sobj = LWGET(OB_NEXT(sobj)) )
                { /* Deselect all but...     */
                if (sobj != obj)
                        objc_dsel(tree, sobj);
                }
        objc_sel(tree, obj);  /* the one being SELECTED  */
        }

        WORD                  /* Mouse button handler    */
form_button(tree, obj, clicks, next_obj, hot_obj)
        LONG    tree;
        WORD    obj, clicks, *next_obj, *hot_obj;
        {
        WORD    flags, state, hibit, texit, sble, dsbld, edit;
        WORD    in_out, in_state;

        /* Get flags and states   */
        flags = LWGET(OB_FLAGS(obj));
        state = LWGET(OB_STATE(obj));
        texit = flags & TOUCHEXIT;
        sble = flags & SELECTABLE;
        dsbld = state & DISABLED;
        edit = flags & EDITABLE;

        /* This is not an      */
        /* interesting object  */
        if (!texit && (!sble || dsbld) && !edit)
                {
                *next_obj = 0;
                return (TRUE);
                }

        /* Preset special flag  */
        if (texit && clicks == 2)
                hibit = 0x8000;
        else
                hibit = 0x0;

        /* Hot stuff!           */
        if (sble && !dsbld)
                {
                /* Process radio buttons*/
                /* immediately!         */ 
                if (flags & RBUTTON)
                        do_radio(tree, obj);
                else if (!texit)
                        {
                        /* Already toggled ? */
                        in_state = (obj == *hot_obj)
                                   ? state
                                   : state ^ SELECTED;        
                        if (!graf_watchbox(tree, obj,
                                           in_state, 
                                           in_state ^ SELECTED))
                                {  /* He gave up...  */
                                *next_obj = 0;
                                *hot_obj = NIL;
                                return (TRUE);
                                }
                        }
                else /* if (texit) */
                        /* Force SELECTED       */
                        if (obj != *hot_obj)
                                objc_toggle(tree, obj);
                }

        /* We're gonna do it! So don't  */
        /* turn it off later.           */
        if (obj == *hot_obj)
                *hot_obj = NIL;

        /* Exit conditions.             */
        if (texit || (flags & EXIT) )
                {
                *next_obj = obj | hibit;
                return (FALSE); /* Time to leave */
                }
        else if (!edit)  /* Clear object unless tabbing  */
                *next_obj = 0;

        return (TRUE);
        }

/*** New forms manager: Entry point and main loop ********/

        WORD
form_do(tree, start_fld)
        REG LONG        tree;
        WORD            *start_fld;
        {
        REG WORD        edit_obj;
        WORD            next_obj, hot_obj, hot_mode;
        WORD            which, cont;
        WORD            idx;
        WORD            mx, my, mb, ks, kr, br;
        GRECT           hot_rect;
        WORD            (*valid)();

        /* Init. editing        */
        next_obj = *start_fld;
        edit_obj = 0;

        /* Initial hotspot cndx */
        hot_obj = NIL; hot_mode = M1_ENTER;
        objc_xywh(tree, ROOT, &hot_rect);

        /* Main event loop      */
        cont = TRUE;
        while (cont)
          {
          /* position cursor on   */
          /*   the selected       */
          /*   editting field     */
          if (edit_obj != next_obj)
          if (next_obj != 0)
                {
                edit_obj = next_obj;
                next_obj = 0;
                objc_edit(tree, edit_obj, 0, &idx, EDINIT);
                }
                /* wait for button or   */
                /* key or rectangle     */
          which = evnt_multi(MU_KEYBD | MU_BUTTON | MU_M1, 
                        0x02, 0x01, 0x01,
                        hot_mode, hot_rect.g_x,  hot_rect.g_y, 
                                   hot_rect.g_w, hot_rect.g_h, 
                        0, 0, 0, 0, 0,
                        0x0L,
                        0, 0,
                        &mx, &my, &mb, &ks, &kr, &br);

          if (which & MU_M1) /* handle rect. event   */
                hot_obj = form_hot(tree, hot_obj, mx, my,
                                   &hot_rect, &hot_mode);
                             /* handle keyboard event*/
          if (which & MU_KEYBD)
                {            /* Control char filter  */
                cont = form_keybd(tree, edit_obj, next_obj,
                                  kr, &next_obj, &kr);
                if (kr && edit_obj) /* Add others to object */
                        objc_edit(tree, edit_obj,
                                  kr, &idx, EDCHAR);
                }
                             /* handle button event  */
          if (which & MU_BUTTON)
                {            /* Which object hit?    */
                next_obj = objc_find(tree, ROOT, 
                                     MAX_DEPTH, mx, my);
                if (next_obj == NIL)
                        next_obj = 0;
                else                 /* Process a click */
                        cont = form_button(tree, next_obj, br, 
                                &next_obj, &hot_obj);
                }
                             /* handle end of field  */
                             /*   clean up           */
          if (!cont || (next_obj != edit_obj && next_obj != 0))
          if (edit_obj != 0) 
                objc_edit(tree, edit_obj, 0, &idx, EDEND);
          }
                             /* If defaulted, may    */
                             /* need to clear hotspot*/
        if (hot_obj != (next_obj & 0x7fff))
        if (hot_obj != NIL)
                objc_toggle(tree, hot_obj);
                             /* return exit object   */
                             /*   hi bit may be set  */
                             /*   if exit obj. was   */
                             /*   double-clicked     */
        *start_fld = edit_obj;
        return(next_obj);
        }

\end{verbatim}
\newpage

\subsubsection{\tt gemcl2.c}
\begin{verbatim} 
>>>>>>>>>>>> Sample Redraw Code <<<<<<<<<<<<<<<<<<<<<<<<<<<<      

        VOID
do_redraw(wh, area)    /* wh = window handle from msg[3] */
        WORD    wh;    /* area = pointer to redraw rect- */
        GRECT   *area; /*   tangle in msg[4] thru msg[7] */
        {
        GRECT   box;

        graf_mouse(M_OFF, 0x0L);
        wind_update(BEG_UPDATE);

        wind_get(wh, WF_FIRSTXYWH,
                 &box.g_x, &box.g_y, &box.g_w, &box.g_h);
        while ( box.g_w && box.g_h )
                {
                /* Full is entire screen */
                if (rc_intersect(full, &box))
                if (rc_intersect(area, &box))
                        {
                        /* Test for window 1 handle */
                        /* AES redraw example       */
                        if (wh == w1_handle)
                                {
                                objc_draw(w1_tree, ROOT,
                                          MAX_DEPTH,
                                          box.g_x, box.g_y,
                                          box.g_w, box.g_h);
                                }
                        /* Test for window 2 handle */
                        /* VDI redraw example       */
                        else if (wh == w2_handle)
                                {
                                set_clip(TRUE, &box);
                                /*  Put VDI drawing calls here */
                                }
                        /* add more windows here */
                        }
                wind_get(wh, WF_NEXTXYWH,
                        &box.g_x, &box.g_y,
                        &box.g_w, &box.g_h);
                }

        wind_update(END_UPDATE);
        graf_mouse(M_ON, 0x0L);
        }

>>>>>>>> Utilities used in do_redraw <<<<<<<<<<<<<<<<<<<<<<<

        VOID
set_clip(clip_flag, area) /* set clip to specified area   */
        WORD    clip_flag;
        GRECT   *area;
        {
        WORD    pxy[4];

        grect_to_array(area, pxy);
        vs_clip(vdi_handle, clip_flag, pxy);
        }

/* convert x,y,w,h to upr lt x,y and    */
/*                    lwr rt x,y        */
        VOID
grect_to_array(area, array)
        GRECT   *area;
        WORD    *array;
        {
        *array++ = area->g_x;
        *array++ = area->g_y;
        *array++ = area->g_x + area->g_w - 1;
        *array = area->g_y + area->g_h - 1;
        }

/* compute intersect of two rectangles  */
        WORD
rc_intersect(p1, p2)
        GRECT   *p1, *p2;
        {
        WORD    tx, ty, tw, th;

        tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w);
        th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h);
        tx = max(p2->g_x, p1->g_x);
        ty = max(p2->g_y, p1->g_y);
        p2->g_x = tx;
        p2->g_y = ty;
        p2->g_w = tw - tx;
        p2->g_h = th - ty;
        return( (tw > tx) && (th > ty) );
        }

>>>>>>>>>>>> "Self-redraw" Utility <<<<<<<<<<<<<<<<<<<<<<

        VOID
send_redraw(wh, p)
        WORD    wh;
        GRECT   *p;
        {
        WORD    msg[8];

        msg[0] = WM_REDRAW; /* Defined in GEMBIND.H     */
        msg[1] = gl_apid;   /* As returned by appl_init */
        msg[2] = 0;
        msg[3] = wh;        /* Handle of window to redraw */
        msg[4] = p->g_x;
        msg[5] = p->g_y;
        msg[6] = p->g_w;
        msg[7] = p->g_h;
        appl_write(gl_apid, 16, &msg);
        /* Use ADDR(msg) for portability */
        }

>>>>>>>>> Utilities for Window Requests <<<<<<<<<<<<<<<<<<<<

        VOID
rc_constrain(pc, pt)
        GRECT           *pc;
        GRECT           *pt;
        {
        if (pt->g_x < pc->g_x)
                pt->g_x = pc->g_x;
        if (pt->g_y < pc->g_y)
                pt->g_y = pc->g_y;
        if ((pt->g_x + pt->g_w) > (pc->g_x + pc->g_w))
                pt->g_x = (pc->g_x + pc->g_w) - pt->g_w;
        if ((pt->g_y + pt->g_h) > (pc->g_y + pc->g_h))
                pt->g_y = (pc->g_y + pc->g_h) - pt->g_h;
        }

/* Snap position x to an n-bit grid         */ 
/* Use n = 16 for horizontal word alignment */
        WORD
align(x,n)              
        WORD    x, n;   
        {
        x += (n >> 2) - 1;   /* Round and... */
        x = n * (x / n);     /* remove residue */
        return (x);
        }       

>>>>>>>> Window full utility <<<<<<<<<<<<<<<<<<<<<<<<<<

/* depending on current window state, make window    */
/* full size -or- return to previous shrunken size   */
/* graf_ calls are optional special effects.         */
        VOID
hndl_full(wh)           
        WORD    wh;     
        {               
        GRECT   prev;
        GRECT   curr;
        GRECT   full;

        wind_get(wh, WF_CXYWH, &curr.g_x, &curr.g_y,
                               &curr.g_w, &curr.g_h );
        wind_get(wh, WF_PXYWH, &prev.g_x, &prev.g_y,
                               &prev.g_w, &prev.g_h );
        wind_get(wh, WF_FXYWH, &full.g_x, &full.g_y,
                               &full.g_w, &full.g_h );

        /* Is full, change to previous          */
        if ( rc_equal(&curr, &full) )
                {
                graf_shrinkbox(prev.g_x, prev.g_y,
                               prev.g_w, prev.g_h,
                               full.g_x, full.g_y,
                               full.g_w, full.g_h);
                wind_set(wh, WF_CXYWH,
                         prev.g_x, prev.g_y,
                         prev.g_w, prev.g_h);
                /* put send_redraw here if you need it */
                }
        else
                { /* is not full, so set to full       */
                graf_growbox(curr.g_x, curr.g_y,
                             curr.g_w, curr.g_h,
                             full.g_x, full.g_y,
                             full.g_w, full.g_h);
                wind_set(wh, WF_CXYWH,
                         full.g_x, full.g_y,
                         full.g_w, full.g_h);
                }
        }

        WORD
rc_equal(p1, p2) /* tests for two rectangles equal */
        GRECT   *p1, *p2;
        {
        if ((p1->g_x != p2->g_x) ||
            (p1->g_y != p2->g_y) ||
            (p1->g_w != p2->g_w) ||
            (p1->g_h != p2->g_h))
                return(FALSE);
        return(TRUE);
        }

\end{verbatim}
\newpage

\subsubsection{\tt gemcl3.c}
\begin{verbatim} 
>>>>>>>>> Basic Dialog Handler <<<<<<<<<<<<<<<<<<<<<<<

        WORD
hndl_dial(tree, def, x, y, w, h)
        LONG    tree;
        WORD    def;
        WORD    x, y, w, h;
        {
        WORD    xdial, ydial, wdial, hdial, exitobj;

        form_center(tree, &xdial, &ydial, &wdial, &hdial);
        form_dial(0, x, y, w, h,
                  xdial, ydial, wdial, hdial);
        form_dial(1, x, y, w, h,
                  xdial, ydial, wdial, hdial);
        objc_draw(tree, ROOT, MAX_DEPTH,
                  xdial, ydial, wdial, hdial);
        exitobj = form_do(tree, def) & 0x7FFF;
        form_dial(2, x, y, w, h,
                  xdial, ydial, wdial, hdial);
        form_dial(3, x, y, w, h,
                  xdial, ydial, wdial, hdial);
        return (exitobj);
        }

>>>>>>>>> Object rectangle utility <<<<<<<<<<<<<<<<<<<<<


/* get x,y,w,h for specified object     */
        VOID
objc_xywh(tree, obj, p)         
        LONG    tree;
        WORD    obj;
        GRECT   *p;
        {
        objc_offset(tree, obj, &p->g_x, &p->g_y);
        p->g_w = LWGET(OB_WIDTH(obj));
        p->g_h = LWGET(OB_HEIGHT(obj));
        }

>>>>>>>>> Object flag utilities <<<<<<<<<<<<<<<<<

/* clear specified bit in object state  */
        VOID
undo_obj(tree, which, bit)      
        LONG    tree;
        WORD    which, bit;
        {
        WORD    state;

        state = LWGET(OB_STATE(which));
        LWSET(OB_STATE(which), state & ~bit);
        }

/* turn off selected bit of spcfd object*/
        VOID
desel_obj(tree, which)
        LONG    tree;
        WORD    which;
        {
        undo_obj(tree, which, SELECTED);
        }

/* set specified bit in object state    */
        VOID
do_obj(tree, which, bit)        
        LONG    tree;
        WORD    which, bit;
        {
        WORD    state;

        state = LWGET(OB_STATE(which));
        LWSET(OB_STATE(which), state | bit);
        }

/* turn on selected bit of spcfd object */
        VOID
sel_obj(tree, which)
        LONG    tree;
        WORD    which;
        {
        do_obj(tree, which, SELECTED);
        }

        BOOLEAN
statep(tree, which, bit)
        LONG    tree;
        WORD    which;
        WORD    bit;
        {
        return ( (LWGET(OB_STATE(which)) & bit) != 0);
        }

        BOOLEAN
selectp(tree, which)
        LONG    tree;
        WORD    which;
        {
        return statep(tree, which, SELECTED);
        }

>>>>>>>>> Sample radio buttons after dialog <<<<<<<<<<<<

        WORD
encode(tree, ob1st, num)
        LONG    tree;
        WORD    ob1st, num;
        {
        for (; num--; )
                if (selectp(ob1st+num))
                        return(num);
        return (-1);
        }


\end{verbatim}
\newpage

\subsubsection{\tt gemcl4.c}
\begin{verbatim} 
>>>>>>>>>> Sample C output file from RCS <<<<<<<<<<<<<<

                        /* (Comments added)     */
BYTE *rs_strings[] = {  /* ASCII data           */
"Title String", 
"Exit",
"Centered Text",
"",
"",
"Butt^Y",
"Tokyo",
"",
"Time: __:__:__",
"999999",
"",
"Time: __:__:__  ",
"999999",
"New York"};

WORD IMAG0[] = {  /* Bitmap for G_IMAGE */
0x7FF, 0xFFFF, 0xFF80, 0xC00, 
0x0, 0xC0, 0x183F, 0xF03F, 
0xF060, 0x187F, 0xF860, 0x1860, 
0x187F, 0xF860, 0x1860, 0x187F, 
0xF860, 0x1860, 0x187F, 0xF860, 
0x1860, 0x187F, 0xF860, 0x1860, 
0x187F, 0xF860, 0x1860, 0x187F, 
0xF860, 0x1860, 0x187F, 0xF860, 
0x1860, 0x187F, 0xF860, 0x1860, 
0x187F, 0xF860, 0x1860, 0x187F, 
0xF860, 0x1860, 0x183F, 0xF03F, 
0xF060, 0xC00, 0x0, 0xC0, 
0x7FF, 0xFFFF, 0xFF80, 0x0, 
0x0, 0x0, 0x3F30, 0xC787, 
0x8FE0, 0xC39, 0xCCCC, 0xCC00, 
0xC36, 0xCFCC, 0xF80, 0xC30, 
0xCCCD, 0xCC00, 0x3F30, 0xCCC7, 
0xCFE0, 0x0, 0x0, 0x0};

WORD IMAG1[] = {   /* Mask for first icon */
0x0, 0x0, 0x0, 0x0, 
0x7FFE, 0x0, 0x1F, 0xFFFF, 
0xFC00, 0xFF, 0xFFFF, 0xFF00, 
0x3FF, 0xFFFF, 0xFFC0, 0xFFF, 
0xFFFF, 0xFFF0, 0x3FFF, 0xFFFF, 
0xFFFC, 0x7FFF, 0xFFFF, 0xFFFE, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0x7FFF, 
0xFFFF, 0xFFFE, 0x3FFF, 0xFFFF, 
0xFFFC, 0xFFF, 0xFFFF, 0xFFF0, 
0x3FF, 0xFFFF, 0xFFC0, 0xFF, 
0xFFFF, 0xFF00, 0x1F, 0xFFFF, 
0xF800, 0x0, 0x7FFE, 0x0};

WORD IMAG2[] = {  /* Data for first icon */
0x0, 0x0, 0x0, 0x0, 
0x3FFC, 0x0, 0xF, 0xC003, 
0xF000, 0x78, 0x180, 0x1E00, 
0x180, 0x180, 0x180, 0x603, 
0x180, 0xC060, 0x1C00, 0x6, 
0x38, 0x3000, 0x18C, 0xC, 
0x60C0, 0x198, 0x306, 0x6000, 
0x1B0, 0x6, 0x4000, 0x1E0, 
0x2, 0xC000, 0x1C0, 0x3, 
0xCFC0, 0x180, 0x3F3, 0xC000, 
0x0, 0x3, 0x4000, 0x0, 
0x2, 0x6000, 0x0, 0x6, 
0x60C0, 0x0, 0x306, 0x3000, 
0x0, 0xC, 0x1C00, 0x0, 
0x38, 0x603, 0x180, 0xC060, 
0x180, 0x180, 0x180, 0x78, 
0x180, 0x1E00, 0xF, 0xC003, 
0xF000, 0x0, 0x3FFC, 0x0};

WORD IMAG3[] = {   /* Mask for second icon */
0x0, 0x0, 0x0, 0x0, 
0x7FFE, 0x0, 0x1F, 0xFFFF, 
0xFC00, 0xFF, 0xFFFF, 0xFF00, 
0x3FF, 0xFFFF, 0xFFC0, 0xFFF, 
0xFFFF, 0xFFF0, 0x3FFF, 0xFFFF, 
0xFFFC, 0x7FFF, 0xFFFF, 0xFFFE, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 
0xFFFF, 0xFFFF, 0xFFFF, 0x7FFF, 
0xFFFF, 0xFFFE, 0x3FFF, 0xFFFF, 
0xFFFC, 0xFFF, 0xFFFF, 0xFFF0, 
0x3FF, 0xFFFF, 0xFFC0, 0xFF, 
0xFFFF, 0xFF00, 0x1F, 0xFFFF, 
0xF800, 0x0, 0x7FFE, 0x0};

WORD IMAG4[] = { /* Data for second icon */
0x0, 0x0, 0x0, 0x0, 
0x3FFC, 0x0, 0xF, 0xC003, 
0xF000, 0x78, 0x180, 0x1E00, 
0x180, 0x180, 0x180, 0x603, 
0x180, 0xC060, 0x1C00, 0x6, 
0x38, 0x3000, 0x18C, 0xC, 
0x60C0, 0x198, 0x306, 0x6000, 
0x1B0, 0x6, 0x4000, 0x1E0, 
0x2, 0xC000, 0x1C0, 0x3, 
0xCFC0, 0x180, 0x3F3, 0xC000, 
0x0, 0x3, 0x4000, 0x0, 
0x2, 0x6000, 0x0, 0x6, 
0x60C0, 0x0, 0x306, 0x3000, 
0x0, 0xC, 0x1C00, 0x0, 
0x38, 0x603, 0x180, 0xC060, 
0x180, 0x180, 0x180, 0x78, 
0x180, 0x1E00, 0xF, 0xC003, 
0xF000, 0x0, 0x3FFC, 0x0};

/* Free string index - unused */
LONG rs_frstr[] = {
0};

/* First entry is index to image data */
BITBLK rs_bitblk[] = {
0L, 6, 24, 0, 0, 0};

/* Free image index - unused */
LONG rs_frimg[] = {
0};

/* First pointer is mask */
/* Second is data, third */
/* is to title string    */
ICONBLK rs_iconblk[] = {                
1L, 2L, 10L, 4096,0,0, 0,0,48,24, 9,24,30,8,
3L, 4L, 17L, 4864,0,0, 0,0,48,24, 0,24,48,8};

/* First pointer is text */
/* Second is template    */
/* Third is validation   */
TEDINFO rs_tedinfo[] = {
2L, 3L, 4L, 3, 6, 2, 0x1180, 0x0, -1, 14,1,
7L, 8L, 9L, 3, 6, 2, 0x2072, 0x0, -3, 11,1,
11L, 12L, 13L, 3, 6, 0, 0x1180, 0x0, -1, 1,15,
14L, 15L, 16L, 3, 6, 1, 0x1173, 0x0, 0, 1,17};

OBJECT rs_object[] = {
/* Pointers are to: */
-1, 1, 3, G_BOX, NONE, OUTLINED, 0x21100L, 0,0, 18,12,
/* rs_strings   */
2, -1, -1, G_STRING, NONE, NORMAL, 0x0L, 3,1, 12,1,
/* rs_strings   */
3, -1, -1, G_BUTTON, 0x7, NORMAL, 0x1L, 5,9, 8,1,
/* rs_strings   */
0, 4, 4, G_BOX, NONE, NORMAL, 0xFF1172L, 3,3, 12,5,
3, -1, -1, G_IMAGE, LASTOB, NORMAL, 0x0L, 3,1, 6,3,
/* rs_bitblk    */
-1, 1, 6, G_BOX, NONE, OUTLINED, 0x21100L, 0,0, 23,12,  
2, -1, -1, G_TEXT, NONE, NORMAL, 0x0L, 0,1, 23,1,
/* rs_tedinfo   */
6, 3, 5, G_IBOX, NONE, NORMAL, 0x1100L, 6,3, 11,5,
4, -1, -1, G_BUTTON, 0x11, NORMAL, 0x5L, 0,0, 11,1,
/* rs_strings   */
5, -1, -1, G_BUTTON, 0x11, NORMAL, 0x6L, 0,2, 11,1,
/* rs_strings   */
2, -1, -1, G_BOXCHAR, 0x11, NORMAL, 0x43FF1400L, 0,4, 11,1,
0, -1, -1, G_BOXTEXT, 0x27, NORMAL, 0x1L, 5,9, 13,1,
/* rs_tedinfo   */
-1, 1, 3, G_BOX, NONE, OUTLINED, 0x21100L, 0,0, 32,11,
2, -1, -1, G_ICON, NONE, NORMAL, 0x0L, 4,1, 6,4,
/* rs_iconblk   */
3, -1, -1, G_FTEXT, EDITABLE, NORMAL, 0x2L, 12,2, 14,1,
/* rs_tedinfo   */
0, 4, 4, G_FBOXTEXT, 0xE, NORMAL, 0x3L, 3,5, 25,4,
/* rs_tedinfo   */
3, -1, -1, G_ICON, LASTOB, NORMAL, 0x1L, 1,0, 6,4};
/* rs_iconblk   */

LONG rs_trindex[] = {  /* Points to start of trees in */
0L,                    /* rs_object                   */
5L,
12L};

struct foobar {        /* Temporary structure used by    */
        WORD    dummy; /* RSCREATE when setting up image */
        WORD    *image;/* pointers.                      */
        } rs_imdope[] = {
0, &IMAG0[0],
0, &IMAG1[0],
0, &IMAG2[0],
0, &IMAG3[0],
0, &IMAG4[0]};
                       /* Counts of structures defined */
#define NUM_STRINGS 18
#define NUM_FRSTR 0
#define NUM_IMAGES 5
#define NUM_BB 1
#define NUM_FRIMG 0
#define NUM_IB 2
#define NUM_TI 4
#define NUM_OBS 17
#define NUM_TREE 3

BYTE pname[] = "DEMO.RSC";

>>>>>>>>>>>>>>> Title change utility <<<<<<<<<<<<<<<<<

        VOID
set_text(tree, obj, str)
        LONG    tree, str;
        WORD    obj;
        {
        LONG    obspec;

        /* Get TEDINFO address  */
        obspec = LLGET(OB_SPEC(obj));
        /* Set new text pointer */
        LLSET(TE_PTEXT(obspec), str);
        /* Set new length       */
        LWSET(TE_TXTLEN(obspec), LSTRLEN(str));
        }

>>>>>>>> Text edit code segment <<<<<<<<<<<<<<<<<<<<

        LONG    tree, obspec;
        BYTE    text[41];

        /* Get tree address  */
        rsrc_gaddr(R_TREE, DIALOG, &tree);

        /* Get TEDINFO address  */
        obspec = LLGET(OB_SPEC(EDITOBJ));       

        /* Set new text pointer */
        LLSET(TE_PTEXT(obspec), ADDR(str));     

        /* Set max length       */
        LWSET(TE_TXTLEN(obspec), 41);           

        /* Make empty string */
        text[0] = '\0';

>>>>>>>> Sample 68K only source code <<<<<<<<<<<<<<<<

        VOID
set_text(tree, obj, str)
        OBJECT  *tree;
        WORD    obj;
        BYTE    *str;
        {
        TEDINFO *obspec;

        /* Get TEDINFO address  */
        obspec = (TEDINFO *) (tree + obj)->ob_spec;

        /* Set new text pointer */
        obspec->te_ptext = str;         

        /* Set new length       */
        obspec->te_txtlen = strlen(str);
        }
>>>>>>>>> Object tree walk utility <<<<<<<<<<<<<<<<<<
 
        VOID
map_tree(tree, this, last, routine)
        LONG            tree;
        WORD            this, last;
        WORD            (*routine)();
        {
        WORD            tmp1;
 
        tmp1 = this;
            /* Initialize to impossible value: */
            /* TAIL won't point to self!       */
            /* Look until final node, or off   */
            /* the end of tree                 */ 
        while (this != last && this != NIL)
            /* Did we 'pop' into this node     */
            /* for the second time?            */
                if (LWGET(OB_TAIL(this)) != tmp1)
                        {
                        tmp1 = this;
                        /* This is a new node       */
                        this = NIL;
                        /* Apply operation, testing  */
                        /* for rejection of sub-tree */
                        if ((*routine)(tree, tmp1))
                                this = LWGET(OB_HEAD(tmp1));
                                /* Subtree path not taken,*/
                                /* so traverse right      */ 
                        if (this == NIL)
                                this = LWGET(OB_NEXT(tmp1));
                        }
                else            /* Revisiting parent:      */
                                /* No operation, move right*/
                        {
                        tmp1 = this;
                        this = LWGET(OB_NEXT(tmp1));
                        }
        }

>>>>> Sample routine to use with map_tree() <<<<<<<<<<<<<<<
 
/* clear specified bit in object state  */
        VOID
undo_obj(tree, which, bit)
        LONG    tree;
        WORD    which, bit;
        {
        WORD    state;

        state = LWGET(OB_STATE(which));
        LWSET(OB_STATE(which), state & ~bit);
        }

/* turn off selected bit of spcfd object*/
        VOID
desel_obj(tree, which)          
        LONG    tree;
        WORD    which;
        {
        undo_obj(tree, which, SELECTED);
        return (TRUE);
        }

>>>>> Sample .ICN Files <<<<<<<<<<
>>>>> Save everything between <<<<
>>>>> lines as CLOCK.ICN <<<<<<<<<

/* GEM Icon Definition: */
#define ICON_W 0x0030
#define ICON_H 0x0018
#define DATASIZE 0x0048

UWORD clock[DATASIZE] =
{ 0x0000, 0x0000, 0x0000, 0x0000, 
  0x3FFC, 0x0000, 0x000F, 0xC003, 
  0xF000, 0x0078, 0x0180, 0x1E00, 
  0x0180, 0x0180, 0x0180, 0x0603, 
  0x0180, 0xC060, 0x1C00, 0x0006, 
  0x0038, 0x3000, 0x018C, 0x000C, 
  0x60C0, 0x0198, 0x0306, 0x6000, 
  0x01B0, 0x0006, 0x4000, 0x01E0, 
  0x0002, 0xC000, 0x01C0, 0x0003, 
  0xCFC0, 0x0180, 0x03F3, 0xC000, 
  0x0000, 0x0003, 0x4000, 0x0000, 
  0x0002, 0x6000, 0x0000, 0x0006, 
  0x60C0, 0x0000, 0x0306, 0x3000, 
  0x0000, 0x000C, 0x1C00, 0x0000, 
  0x0038, 0x0603, 0x0180, 0xC060, 
  0x0180, 0x0180, 0x0180, 0x0078, 
  0x0180, 0x1E00, 0x000F, 0xC003, 
  0xF000, 0x0000, 0x3FFC, 0x0000
};

>>>>> End of CLOCK.ICN <<<<<<<<<<<<<<<<<

>>>>> Symbol definitions <<<<<<<<<<<<<<<

/* Window parts */
#define NAME 0x0001
#define CLOSER 0x0002
#define FULLER 0x0004
#define MOVER 0x0008
#define INFO 0x0010
#define SIZER 0x0020
#define UPARROW 0x0040
#define DNARROW 0x0080
#define VSLIDE 0x0100
#define LFARROW 0x0200
#define RTARROW 0x0400
#define HSLIDE 0x0800

/* wind_get/set parameters */
#define WF_KIND 1
#define WF_NAME 2
#define WF_INFO 3
#define WF_WXYWH 4
#define WF_CXYWH 5
#define WF_PXYWH 6
#define WF_FXYWH 7
#define WF_HSLIDE 8
#define WF_VSLIDE 9
#define WF_TOP 10
#define WF_FIRSTXYWH 11
#define WF_NEXTXYWH 12
#define WF_NEWDESK 14
#define WF_HSLSIZ 15
#define WF_VSLSIZ 16

/* window messages      */
#define WM_REDRAW 20
#define WM_TOPPED 21
#define WM_CLOSED 22
#define WM_FULLED 23
#define WM_ARROWED 24
#define WM_HSLID 25
#define WM_VSLID 26
#define WM_SIZED 27
#define WM_MOVED 28
#define WM_NEWTOP 29

/* arrow messages       */
#define WA_UPPAGE 0
#define WA_DNPAGE 1
#define WA_UPLINE 2
#define WA_DNLINE 3
#define WA_LFPAGE 4
#define WA_RTPAGE 5
#define WA_LFLINE 6
#define WA_RTLINE 7

/* Redraw definitions   */
#define R_TREE 0
#define ROOT 0
#define MAX_DEPTH 8

/* update flags         */
#define END_UPDATE 0
#define BEG_UPDATE 1
#define END_MCTRL  2
#define BEG_MCTRL  3

/* Mouse state changes   */
#define M_OFF 256
#define M_ON 257

/* Object flags          */
#define NONE       0x0
#define SELECTABLE 0x1
#define DEFAULT    0x2
#define EXIT       0x4
#define EDITABLE   0x8
#define RBUTTON   0x10

/* Object states        */
#define SELECTED  0x1
#define CROSSED   0x2
#define CHECKED   0x4
#define DISABLED  0x8
#define OUTLINED 0x10
#define SHADOWED 0x20

#define G_BOX     20
#define G_TEXT    21
#define G_BOXTEXT 22
#define G_IMAGE   23
#define G_IBOX    25
#define G_BUTTON  26
#define G_BOXCHAR 27
#define G_STRING  28
#define G_FTEXT   29
#define G_FBOXTEXT 30
#define G_ICON    31
#define G_TITLE   32

/* Data structures      */
typedef struct grect
        {
        int     g_x;
        int     g_y;
        int     g_w;
        int     g_h;
        } GRECT;

typedef struct object
        {
        int          ob_next; /* -> object's next sibling     */
        int          ob_head; /* -> head of object's children */
        int          ob_tail; /* -> tail of object's children */
        unsigned int ob_type; /* type of object- BOX, CHAR,...*/
        unsigned int ob_flags;/* flags                        */
        unsigned int ob_state;/* state- SELECTED, OPEN, ...   */
        long         ob_spec; /* "out"- -> anything else      */
        int          ob_x;    /* upper left corner of object  */
        int          ob_y;    /* upper left corner of object  */
        int          ob_width;/* width of obj                 */
        int          ob_height;/* height of obj               */
        } OBJECT;

typedef struct text_edinfo
        {
        long te_ptext;    /* ptr to text (must be 1st)  */
        long te_ptmplt;   /* ptr to template            */
        long te_pvalid;   /* ptr to validation chrs.    */
        int  te_font;     /* font                       */
        int  te_junk1;    /* junk word                  */
        int  te_just;     /* justification- left, right */
        int  te_color;    /* color information word     */
        int  te_junk2;    /* junk word                  */
        int  te_thickness;/* border thickness           */
        int  te_txtlen;   /* length of text string      */
        int  te_tmplen;   /* length of template string  */
        } TEDINFO;

/* "Portable" data definitions */
#define OB_NEXT(x) (tree + (x) * sizeof(OBJECT) + 0)
#define OB_HEAD(x) (tree + (x) * sizeof(OBJECT) + 2)
#define OB_TAIL(x) (tree + (x) * sizeof(OBJECT) + 4)
#define OB_TYPE(x) (tree + (x) * sizeof(OBJECT) + 6)
#define OB_FLAGS(x) (tree + (x) * sizeof(OBJECT) + 8)
#define OB_STATE(x) (tree + (x) * sizeof(OBJECT) + 10)
#define OB_SPEC(x) (tree + (x) * sizeof(OBJECT) + 12)
#define OB_X(x) (tree + (x) * sizeof(OBJECT) + 16)
#define OB_Y(x) (tree + (x) * sizeof(OBJECT) + 18)
#define OB_WIDTH(x) (tree + (x) * sizeof(OBJECT) + 20)
#define OB_HEIGHT(x) (tree + (x) * sizeof(OBJECT) + 22)

#define TE_PTEXT(x)  (x)
#define TE_TXTLEN(x)  (x + 24)

\end{verbatim}
\newpage

\subsubsection{\tt gemcl5.c}
\begin{verbatim} 
>>>>>>>>> MFDB Structure <<<<<<<<<<<<<<<<<<<<<<<<

/* Memory Form Definition Block */
typedef struct fdbstr
{
    long   fd_addr;    /* Form address                 */
    int    fd_w;       /* Form width in pixels         */
    int    fd_h;       /* Form height in pixels        */
    int    fd_wdwidth; /* Form width in memory words   */
    int    fd_stand;   /* Standard form flag           */
    int    fd_nplanes; /* Number of color planes       */
    int    fd_r1;      /* Dummy locations:             */
    int    fd_r2;      /* Reserved for future use      */
    int    fd_r3;
} MFDB;

>>>>>> Resource Transform Utilities <<<<<<<<<<<<<<<<<<<
/*------------------------------*/
/*      vdi_fix                 */
/*------------------------------*/
/* This routine loads the MFDB  */
/* Input values are the MFDB's  */
/* address, the form's address, */
/* the form's width in bytes,   */
/* and the height in pixels     */

        VOID
vdi_fix(pfd, theaddr, wb, h)
        MFDB            *pfd;
        LONG            theaddr;
        WORD            wb, h;
        {                               
        pfd->fww = wb >> 1;
        pfd->fwp = wb << 3;
        pfd->fh = h;
        pfd->np = 1;     /* Monochrome assumed */
        pfd->mp = theaddr;
        }

/*------------------------------*/
/*      vdi_trans               */
/*------------------------------*/
/* Transform the standard form  */
/* pointed at by saddr and      */
/* store in the form at daddr   */
/* Byte widths and pixel height */
/* are given                    */

        WORD
vdi_trans(saddr, swb, daddr, dwb, h)
        LONG            saddr;          
        UWORD           swb;            
        LONG            daddr;          
        UWORD           dwb;            
        UWORD           h;
        {
        /* These are on-the-fly MFDBs  */
        MFDB            src, dst;

        /* Load the source MFDB        */
        vdi_fix(&src, saddr, swb, h);

        /* Set it's std form flag      */
        src.ff = TRUE;

        /* Load the destination MFDB   */
        vdi_fix(&dst, daddr, dwb, h);

        /* Clear the std flag          */
        dst.ff = FALSE;

        /* Call the VDI        */
        vr_trnfm(vdi_handle, &src, &dst );
        }

/*------------------------------*/
/*      trans_bitblk            */
/*------------------------------*/
/* Transform the image belonging */
/* to the bitblk pointed to by   */
/* obspec.  This routine may also*/
/* be used with free images      */
        VOID
trans_bitblk(obspec)
        LONG    obspec;                 
        {                               
        LONG    taddr;                  
        WORD    wb, hl;

        /* Get and validate image address */
        /* Extract image dimensions       */
        if ( (taddr = LLGET(BI_PDATA(obspec))) == -1L)
                return;
        wb = LWGET(BI_WB(obspec));
        hl = LWGET(BI_HL(obspec));
        vdi_trans(taddr, wb, taddr, wb, hl);
        /* Perform a transform    */
        /* in place               */
        }

/*------------------------------*/
/*      trans_obj               */
/*------------------------------*/
/* Examine the input object.  If  */
/* it is an icon or image, trans- */
/* form the associated raster     */
/* forms in place.                */
/* This routine may be used with  */
/* map_tree() to transform an     */
/* entire resource tree           */

        VOID
trans_obj(tree, obj)                    
        LONG    tree;                   
        WORD    obj;                    
        {                               
        WORD    type, wb, hl;           
        LONG    taddr, obspec;          
                                        

        /* Load object type */
        type = LLOBT(LWGET(OB_TYPE(obj)));              

        /* Load and check   */
        if ( (obspec = LLGET(OB_SPEC(obj))) == -1L)     
                return (TRUE);                          
                /* ob_spec pointer  */
        switch (type) {
                case G_IMAGE:
                        trans_bitblk(obspec);           
                        /* Transform image  */
                        return (TRUE);
                case G_ICON:                            
                        /* Load icon size   */
                        hl = LWGET(IB_HICON(obspec));
                        wb = (LWGET(IB_WICON(obspec)) + 7)
                              >> 3;
                                                        
                        /* Transform data   */
                        if ( (taddr = LLGET(IB_PDATA(obspec)))
                              != -1L)
                                vdi_trans(taddr, wb, 
                                          taddr, wb, hl);
                                                        
                        /* Transform mask   */
                        if ( (taddr = LLGET(IB_PMASK(obspec)))
                              != -1L)
                                vdi_trans(taddr, wb,
                                          taddr, wb, hl);
                        return (TRUE);
                default:
                        return (TRUE);
                }
        }

>>>>>>>  Macro definitions for the code above <<<<<<<<<<<<<

#define BI_PDATA(x)     (x)
#define BI_WB(x)        (x + 4)
#define BI_HL(x)        (x + 6)
#define OB_TYPE(x)      (tree + (x) * sizeof(OBJECT) + 6)
#define OB_SPEC(x)      (tree + (x) * sizeof(OBJECT) + 12)
#define IB_PMASK(x)     (x)
#define IB_PDATA(x)     (x + 4)
#define IB_WICON(x)     (x + 22)
#define IB_HICON(x)     (x + 24)

>>>>>>>>> VDI Copy Mode Table <<<<<<<<<<<<<<<<<<<<<<<<<<<<

Symbols: N = new destination pixel value (0 or 1)
         D = old destination pixel value (0 or 1)
         S = source pixel value (0 or 1)
         ~ = Boolean not (inversion)
         & = Boolean and
         | = Boolean or
         ^ = Boolean xor (exclusive-or)

Mode Number     Action
----------      ------
    0           N = 0           (USE V_BAR INSTEAD)
    1           N = S & D
    2           N = S & ~D
    3           N = S           (REPLACE)
    4           N = ~S & D      (ERASE)
    5           N = D           (USELESS)
    6           N = S ^ D       (XOR)
    7           N = S | D       (TRANSPARENT)
    8           N = ~ (S | D)
    9           N = ~ (S ^ D)
   10           N = ~D          (USE V_BAR INSTEAD)
   11           N = S | ~D
   12           N = ~S
   13           N = ~S | D      (REVERSE TRANSPARENT)
   14           N = ~ (S & D)
   15           N = 1           (USE V_BAR INSTEAD)

>>>>>>>> END OF DOWNLOAD <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

\end{verbatim}
\newpage

\subsubsection{\tt gemcl6.c}
\begin{verbatim} 
>>>>>>>> Sample Menu Tree <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

/* ROOT */
-1, 1, 6, G_IBOX, NONE, NORMAL, 0x0L, 0,0, 80,25,       

/* THE BAR */
6, 2, 2, G_BOX, NONE, NORMAL, 0x1100L, 0,0, 80,513,     

/* THE ACTIVE */
1, 3, 5, G_IBOX, NONE, NORMAL, 0x0L, 2,0, 20,769,       

/* Title #1 */
4, -1, -1, G_TITLE, NONE, NORMAL, 0x0L, 0,0, 6,769,     

/* Title #2 */
5, -1, -1, G_TITLE, NONE, NORMAL, 0x1L, 6,0, 6,769,     

/* Title #3 */
2, -1, -1, G_TITLE, NONE, NORMAL, 0x2L, 12,0, 8,769,    

/* THE SCREEN */
0, 7, 22, G_IBOX, NONE, NORMAL, 0x0L, 0,769, 80,19,     

/* Drop-down #1 */
16, 8, 15, G_BOX, NONE, NORMAL, 0xFF1100L, 2,0, 20,8,   

/* About... entry */
9, -1, -1, G_STRING, NONE, NORMAL, 0x3L, 0,0, 19,1,     

/* Desk acc entries */
10, -1, -1, G_STRING, NONE, DISABLED, 0x4L, 0,1, 20,1,
11, -1, -1, G_STRING, NONE, NORMAL, 0x5L, 0,2, 20,1,    
12, -1, -1, G_STRING, NONE, NORMAL, 0x6L, 0,3, 20,1,
13, -1, -1, G_STRING, NONE, NORMAL, 0x7L, 0,4, 20,1,
14, -1, -1, G_STRING, NONE, NORMAL, 0x8L, 0,5, 20,1,
15, -1, -1, G_STRING, NONE, NORMAL, 0x9L, 0,6, 20,1,
7, -1, -1, G_STRING, NONE, NORMAL, 0xAL, 0,7, 20,1,

/* Drop-down #2 */
22, 17, 21, G_BOX, NONE, NORMAL, 0xFF1100L, 8,0, 13,5,  
18, -1, -1, G_STRING, NONE, NORMAL, 0xBL, 0,0, 13,1,
19, -1, -1, G_STRING, NONE, DISABLED, 0xCL, 0,1, 13,1,
20, -1, -1, G_STRING, NONE, NORMAL, 0xDL, 0,4, 13,1,
21, -1, -1, G_STRING, NONE, NORMAL, 0xEL, 0,2, 13,1,
16, -1, -1, G_STRING, NONE, DISABLED, 0xFL, 0,3, 13,1,

/* Drop down #3 */
6, 23, 25, G_BOX, NONE, NORMAL, 0xFF1100L, 14,0, 26,3,  
24, -1, -1, G_STRING, NONE, NORMAL, 0x10L, 0,2, 26,1,
25, -1, -1, G_STRING, NONE, NORMAL, 0x11L, 0,0, 26,1,
22, -1, -1, G_STRING, LASTOB, DISABLED, 0x12L, 0,1, 26,1

>>>>>>>>>>>> Menu enable/disable utility <<<<<<<<<<<<<<<

/*------------------------------*/
/*      undo_obj                */
/*------------------------------*/
        VOID
undo_obj(tree, which, bit)
        LONG    tree;
        WORD    which;
        UWORD   bit;
        {
        WORD    state;

        state = LWGET(OB_STATE(which));
        LWSET(OB_STATE(which), state & ~bit);
        }

/*------------------------------*/
/*      enab_obj                */
/*------------------------------*/
        WORD
enab_obj(tree, which)
        LONG    tree;
        WORD    which;
        {
        undo_obj(tree, which, (UWORD) DISABLED);
        return (TRUE);
        }

/*------------------------------*/
/*      do_obj                  */
/*------------------------------*/
        VOID
do_obj(tree, which, bit)
        LONG    tree;
        WORD    which;
        UWORD   bit;
        {
        WORD    state;

        state = LWGET(OB_STATE(which));
        LWSET(OB_STATE(which), state | bit);
        }

/*------------------------------*/
/*      disab_obj               */
/*------------------------------*/
        WORD
disab_obj(tree, which)
        LONG    tree;
        WORD    which;
        {
        do_obj(tree, which, (UWORD) DISABLED);
        return (TRUE);
        }

/*------------------------------*/
/*      set_menu                */
/*------------------------------*/
/* change[0] TRUE selects all entries*/
/* FALSE deselects all.  Change list */
/* of items is then toggled.         */

        VOID
set_menu(tree, change)                  
        LONG    tree;                   
        WORD    *change;                
        {
        WORD    dflt, screen, drop, obj;

        /* What is default?   */
        dflt = *change++;                       

        /* Get SCREEN         */
        screen = LWGET(OB_TAIL(ROOT));          

        /* Get DESK drop-down */
        /* and skip it        */
        drop = LWGET(OB_HEAD(screen));          
                                                
        for (; (drop = LWGET(OB_NEXT(drop))) != screen; )
                {
                obj = LWGET(OB_HEAD(drop));
                if (obj != NIL)
                if (dflt)
                        map_tree(tree, obj, drop, enab_obj);
                else
                        map_tree(tree, obj, drop, disab_obj);
                }

        for (; *change; change++)
                if (dflt)
                        disab_obj(tree, *change);
                else
                        enab_obj(tree, *change);
        }

>>>>>> Definitions used in this article <<<<<<<<<<<<<<<<<<<<<<

#define ROOT 0

#define G_IBOX    25
#define G_STRING  28
#define G_TITLE   32

#define R_TREE     0

#define MN_SELECTED 10

#define CHECKED   0x4
#define DISABLED  0x8

#define OB_NEXT(x) (tree + (x) * sizeof(OBJECT) + 0)
#define OB_HEAD(x) (tree + (x) * sizeof(OBJECT) + 2)
#define OB_TAIL(x) (tree + (x) * sizeof(OBJECT) + 4)
#define OB_TYPE(x) (tree + (x) * sizeof(OBJECT) + 6)
#define OB_FLAGS(x) (tree + (x) * sizeof(OBJECT) + 8)
#define OB_STATE(x) (tree + (x) * sizeof(OBJECT) + 10)
#define OB_SPEC(x) (tree + (x) * sizeof(OBJECT) + 12)
#define OB_X(x) (tree + (x) * sizeof(OBJECT) + 16)
#define OB_Y(x) (tree + (x) * sizeof(OBJECT) + 18)
#define OB_WIDTH(x) (tree + (x) * sizeof(OBJECT) + 20)
#define OB_HEIGHT(x) (tree + (x) * sizeof(OBJECT) + 22)

#define M_OFF     256
#define M_ON      257

\end{verbatim}
\newpage

\subsubsection{\tt gemcl7.c}
\begin{verbatim} 
>>>>>> Sample code for initializing User Objects <<<<<<<<

/* APPLBLK defined in OBDEFS.H  */
GLOBAL  USERBLK extobjs[MAX_OBJS];

/* Set MAX_OBJS to total user   */
/* objects in resource          */
GLOBAL  WORD    n_extobjs;

/* Scan whole resource for user   */
/* objects.  Uses map_tree()      */
/* from GEMCL5.C                  */

        VOID
obj_init()                              
        {                               
        LONG    tree, obspec;           
        WORD    itree, i, obj;

        /* Replace TREE0 with your first  */
        n_extobjs = 0;                  
                                        
        /* tree, TREEN with the last      */
        for (itree = TREE0; itree <= TREEN; itree++)
                {
                rsrc_gaddr(R_TREE, itree, &tree);
                map_tree(tree, ROOT, NIL, fix_obj);
                }
        }

        WORD
fix_obj(tree, obj)     /* Code to check and fix up */
        LONG    tree;  /* a user defined object   */
        WORD    obj;
        {
        WORD    hibyte;

        hibyte = LWGET(OB_TYPE(obj)) & 0xff00;/* check extended */
        if (!hibyte)                          /* type - if none */
                return (TRUE);                /* ignore object  */

        /* set drawcode   */
        extobjs[n_extobjs].ub_code = dr_code;

        /* copy obspec    */
        extobjs[n_extobjs].ub_parm = LLGET(OB_SPEC(obj));

        /* point obspec   */
        LLSET(OB_SPEC(obj), ADDR(&extobjs[n_extobjs]));

        /* to userblk &   */
        LWSET(OB_TYPE(obj), G_USERDEF | hibyte);

        /* patch type     */
        n_extobjs++;

        return (TRUE);

        }
        
>>>>>>>>> Sample User Object Drawing Code <<<<<<<<<<<<<<<<<<<<
>>>>>>>>> Implements Rounded Box based    <<<<<<<<<<<<<<<<<<<<
>>>>>>>>> on G_BOX type                   <<<<<<<<<<<<<<<<<<<<

/* Sample user object drawing   */
/* code.  Caution: NOT portable */
/* to Intel small data models   */

        WORD
dr_code(pb)                             
        PARMBLK *pb;                    
        {                               
        LONG    tree, obspec;
        WORD    slct, flip, type, ext_type, flags;
        WORD    pxy[4];
        WORD    bgc, interior, style, bdc, width, chc;
        
        tree = pb->pb_tree;
        /* original obspec from USERBLK  */
        obspec = LLGET(pb->pb_parm);
        ext_type = LHIBT(LWGET(OB_TYPE(pb->pb_obj)));
        slct = SELECTED &
               pb->pb_currstate;
        flip = SELECTED &
               (pb->pb_currstate ^ pb->pb_prevstate);
        /* These two routines in GEMCL9.C */
        set_clip(TRUE, &pb->pb_xc);
        grect_to_array(&pb->pb_x, pxy); 

        switch (ext_type) {
                case 1:    /* Rounded box       */
                           /* Crack color word  */
                        get_colrwd( obspec,
                                    &bgc, &style, &interior,
                                    &bdc, &width, &chc );
                           /* For select effect, use char color */
                           /* In place of background            */
                        if (slct)
                                bgc = chc;
                           /* Fill in background                */
                        rr_fill( MD_REPLACE,
                                 (width ? 0 : 1),
                                 bgc, interior, style, pxy );
                           /* Do perimeter if needed            */
                           /* rr_perim is in GEMCL9.C           */
                        if (width && !flip)
                                {
                                pxy[0] -= width; pxy[2] += width; 
                                rr_perim(MD_REPLACE,
                                         bdc, FIS_SOLID, 
                                         width, pxy);
                                }
                        break;
                default:   /* Add more types here              */
                        break;
                }
        return (0);
        }

        VOID /* Cracks the obspec color word    */
get_colrwd(obspec, bgc, style, interior, bdc, width, chc)
        LONG    obspec;
        WORD    *bgc, *style, *interior,
                *bdc, *width, *chc, *chmode;
        {
        WORD    colorwd;

        colorwd = LLOWD(obspec);
        *bgc = colorwd & 0xf;
        *style = (colorwd & 0x70) >> 4;
        if ( !(*style) )
                *interior = 0;
        else if (*style == 7)
                *interior = 1;
        else if (colorwd & 0x80) /* HACK: Uses char writing mode */
                *interior = 3;   /* bit to sel. alt. interior    */
        else                     /* styles!                      */
                *interior = 2;
        *bdc = (colorwd & 0xf000) >> 12;

        *width = LHIWD(obspec) & 0xff;
        if (*width > 127)
                *width = 256 - *width;

        /* VDI only renders odd */
        /* widths!              */
        if (*width && !(*width & 0x1))
                (*width)--;                     

        /* used for select effect */
        *chc = (colorwd & 0x0f00) >> 8; 
        }

        VOID /* Fill a rounded rectangle     */
rr_fill(mode, perim, color, interior, style, pxy)
        WORD    mode, perim, color, style,
                interior, *pxy;
        {
        vswr_mode(vdi_handle, mode);
        vsf_color(vdi_handle, color);
        vsf_style(vdi_handle, style);
        vsf_interior(vdi_handle, interior);
        vsf_perimeter(vdi_handle, perim);
        v_rfbox(vdi_handle, pxy);
        }


\end{verbatim}
\newpage

\subsubsection{\tt gemcl8.c}
\begin{verbatim} 
>>>>>>> Routines to set clip to a GRECT <<<<<<<<<<<

/* convert x,y,w,h to upr lt x,y and    */
/*                    lwr rt x,y        */

        VOID
grect_to_array(area, array)     
        GRECT   *area;          
        WORD    *array;
        {
        *array++ = area->g_x;
        *array++ = area->g_y;
        *array++ = area->g_x + area->g_w - 1;
        *array = area->g_y + area->g_h - 1;
        }

/* set clip to specified area           */

        VOID
set_clip(clip_flag, s_area)     
        WORD    clip_flag;
        GRECT   *s_area;
        {
        WORD    pxy[4];

        grect_to_array(s_area, pxy);
        vs_clip(vdi_handle, clip_flag, pxy);
        }

>>>>>> Routines to set attributes before output <<<<<<<<

/* Draw a rounded    */
/* rectangle outline */

        VOID
rr_perim(mode, color, type, width, pxy)      
        WORD    mode, color, width, *pxy;    
        {
        vswr_mode(vdi_handle, mode);
        vsl_color(vdi_handle, color);
        vsl_type(vdi_handle, type);
        vsl_width(vdi_handle, width);
        v_rbox(vdi_handle, pxy);
        vswr_mode(vdi_handle, MD_REPLACE);
        }

/* Draw a polygonal */
/* figure           */

        VOID
pl_perim(mode, type, color, width, npts, pxy)     
                                                  
        WORD    mode, type, color, width, npts, *pxy;
        {
        vswr_mode(vdi_handle, mode);
        vsl_type(vdi_handle, type);
        vsl_color(vdi_handle, color);
        vsl_width(vdi_handle, width);
        v_pline(vdi_handle, npts, pxy);
        }

/* Draw a filled polygonal area */

        VOID
pl_fill(mode, perim, color, interior, style, npts, pxy)
        WORD
        mode, perim, color, interior, style, npts, *pxy;

        {
        vswr_mode(vdi_handle, mode);
        vsf_color(vdi_handle, color);
        vsf_style(vdi_handle, style);
        vsf_interior(vdi_handle, interior);
        vsf_perimeter(vdi_handle, perim);
        v_fillarea(vdi_handle, npts, pxy);
        }

/* Draw a filled rectangle    */

        VOID                  
rect_fill(mode, perim, color, interior, style, pxy)
        WORD    mode, perim, color, style, interior, *pxy;
        {
        vswr_mode(vdi_handle, mode);
        vsf_color(vdi_handle, color);
        vsf_style(vdi_handle, style);
        vsf_interior(vdi_handle, interior);
        vsf_perimeter(vdi_handle, perim);
        vr_recfl(vdi_handle, pxy);
        }

>>>> Demonstration of byte alignment of window interior <<<<

#define FEATURES 0x0fef /* what border features are used   */
WORD    msg[8];         /* message from evnt_multi         */
GRECT   work_area;      /* defines working area            */
WORD    w_hndl;         /* handle for window being changed */

        wind_calc(1, FEATURES, msg[4], msg[5], msg[6], msg[7], 
                &work_area.g_x, &work_area.g_y, &work_area.g_w, 
                &work_area.g_h);
        work_area.g_x = align_x(work_area.g_x);
        work_area.g_w = align_x(work_area.g_w);
        wind_calc(0, FEATURES, work_area.g_x, work_area.g_y, 
                work_area.g_w, work_area.g_h, &msg[4], &msg[5], 
                &msg[6], &msg[7]); 
        wind_set(w_hndl, WF_CXYWH,
                 msg[4], msg[5], msg[6], msg[7]);

>>>>>>>>>> Subroutine for above <<<<<<<<<<<<<<<<<<<<<<<

/* forces word alignment for column position    */
/*   rounding to nearest word           */

        WORD
align_x(x)              
        WORD    x;              
        {
        return((x & 0xfff0) + ((x & 0x0008) ? 0x0010 : 0));
        }       

>>>>>>>>>>> Standard v_gtext binding <<<<<<<<<<<<<<<<<

        WORD
v_gtext( handle, x, y, string)
        WORD handle, x, y;
        BYTE *string;
        {
        WORD i;
        ptsin[0] = x;
        ptsin[1] = y;
        i = 0;

        /* Copy characters to intin    */
        /* There is NO error checking! */
        while (intin[i++] = *string++) 
                ;                       
        contrl[0] = 8;
        contrl[1] = 1;
        contrl[3] = --i;
        contrl[6] = handle;
        vdi();
        }
\end{verbatim}

\newpage

% End of Appendix A
